package com.uxsino.commons.logicSelector.propFunction;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import com.uxsino.commons.logicSelector.Prop;

public class PropWildcardValueFunction implements IPropValueFunction {

    private Map<Class<?>, IPropValueFunction> cachedValueFunctions;

    private String propName;

    private String index;

    protected PropWildcardValueFunction(String propName, String index) {
        this.propName = propName;
        this.index = index;
        cachedValueFunctions = new HashMap<>();
    }

    private IPropValueFunction getRealFunction(Class<?> cls) {
        if (cachedValueFunctions.containsKey(cls)) {
            return cachedValueFunctions.get(cls);
        }

        IPropValueFunction func = _createValueFunction(cls);
        cachedValueFunctions.put(cls, func);
        return func;
    }

    public static IPropValueFunction creaetValueFunction(String propName, String index, Class<?> cls) {

        if (cls == null) {
            return new PropWildcardValueFunction(propName, index);
        }
        return _createValueFunction(propName, index, cls);
    }

    private IPropValueFunction _createValueFunction(Class<?> cls) {
        return _createValueFunction(propName, index, cls);
    }

    private static IPropValueFunction _createValueFunction(String propName, String index, Class<?> cls) {

        IPropValueFunction func;
        for (Method method : cls.getMethods()) {
            Prop p = method.getAnnotation(Prop.class);
            if (p == null || p.name().compareTo(propName) != 0)
                continue;
            PropMethodValueFunction fm = new PropMethodValueFunction(method);
            if (fm.isIndexed()) {
                fm.setIndex(index);
                func = fm;
            } else {
                if (index == null) {
                    func = fm;
                    // throw new IllegalArgumentException("prop " + propName + "
                    // need index.");
                } else
                    func = new IndexedValueFunction(fm, index);
            }
            return func;
        }

        for (Field fld : cls.getFields()) {
            Prop p = fld.getAnnotation(Prop.class);
            if (p == null || p.name().compareTo(propName) != 0) {
                continue;
            }
            PropFieldValueFunction ff = new PropFieldValueFunction(fld);
            if (index != null) {
                func = new IndexedValueFunction(ff, index);
            } else {
                func = ff;
            }
            return func;
        }

        PropProxyValueFunction pp = PropProxyValueFunction.create(cls, propName);
        if (pp != null) {
            if (index != null)
                return new IndexedValueFunction(pp, index);
            else
                return pp;
        }
        throw new IllegalArgumentException("can not found prop " + propName);
    }

    @Override
    public Object get(Object object) {

        if (object == null)
            return null;

        IPropValueFunction func = getRealFunction(object.getClass());

        if (func != null)
            return func.get(object);
        return null;
    }

}
